package cn.tangsihai.hrm.service.impl;

import cn.tangsihai.hrm.dto.SMSCodeDto;
import cn.tangsihai.hrm.exceptions.GlobalException;
import cn.tangsihai.hrm.mark.MarkCode;
import cn.tangsihai.hrm.service.IVerifyCodeService;
import cn.tangsihai.hrm.to.SendSMSCode;
import cn.tangsihai.hrm.util.AssertUtil;
import cn.tangsihai.hrm.util.StrUtils;
import cn.tangsihai.hrm.util.VerifyCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    /**
     * 获取图片验证码
     *      1.校验参数合理性
     *      2.获取随机4位验证码
     *      3.以key为key，验证码为value存入redis中，设置过期时间为3分钟
     *      4.获取图片将验证码与图片合并,将合并后的图片转换为Base64位字符串返回
     * @param key
     * @return
     */
    @Override
    public String getImageCode(String key) {
       // 1.校验参数合理性
        AssertUtil.isNotEmpty(key, "非法请求");

       // 2.获取随机4位验证码
        String code = StrUtils.getComplexRandomString(4);

        // 3.以key为key，验证码为value存入redis中，设置过期时间为3分钟  整合redis
        redisTemplate.opsForValue().set(key, code, 3L, TimeUnit.MINUTES);

       // 4.获取图片,将验证码与图片合并
        try {
            String imgVerifyCode = VerifyCodeUtils.imgVerifyCode(135, 36, code);
            // 6.将合并后的图片转换为Base64位字符串返回
            return imgVerifyCode;
        } catch (IOException e) {
            e.printStackTrace();
            throw new GlobalException(e.getMessage());
        }
    }

    /**
     * 发送短信验证码
     *      1.校验参数合理性：JSR303是否非空，手机号是否已注册（可以省，在注册中做）
     *      2.判断验证码：不存在：验证码过期，
     *      3.存在：没过期，判断验证码是否相同，不相同：返回信息
     *      4.相同：发送短信验证码
     *      5.判断用户是否是第一次进来
     *      5.1 是：获取短信验证码smsCode，调用发送接口发送，将信息存入redis中 key：register:sms:mobile, value：smsCode
     *      5.2 不是：从redis中取出验证码再次发送出去，刷新时间--重新存一次
     *      6.保存发送信息到数据库表中
     *      7.删除图片验证码
     * @param dto
     */
    @Override
    @Transactional
    public void sendSmsCode(SMSCodeDto dto) {
        String imageCode = dto.getImageCode();
        String imageCodeKey = dto.getImageCodeKey();
        String mobile = dto.getMobile();
        // 1.校验参数合理性：JSR303是否非空，手机号是否已注册
        // 2.判断验证码：不存在：验证码过期，
        String imageCodeFromRedis = (String) redisTemplate.opsForValue().get(imageCodeKey);
        AssertUtil.isNotEmpty(imageCodeFromRedis, "验证码过期，请从新获取");

        // 3.存在：没过期，判断验证码是否相同，不相同：返回信息
        AssertUtil.isEqualsIgnoreCase(imageCode, imageCodeFromRedis, "验证码错误");

        // 4.相同：发送短信验证码
        // 5.判断用户是否是第一次进来
        String smsCode = null;
        String key = String.format(MarkCode.REGISTER_PHONE_REDIS_KEY, mobile);
        SendSMSCode smsCodeFromRedis = (SendSMSCode)redisTemplate.opsForValue().get(key);
        if (smsCodeFromRedis != null){ // 不是：从redis中取出验证码
            // 判断是否超过60s
            boolean judge = (new Date().getTime() - smsCodeFromRedis.getSendTime()) / 1000 < 60;
            // 没超过：等60s后再发送
            AssertUtil.isFalse(judge, "操作频繁，请等待");
            // 超过：把相同的验证码再发一次
            smsCode = smsCodeFromRedis.getSmsCode();
        } else {
            // 5.2 是：获取短信验证码smsCode，调用发送接口发送，将信息存入redis中 key：register:sms:mobile, value：smsCode
            smsCode = StrUtils.getComplexRandomString(4);
        }
        //TODO 调用发送接口发送
        // 将信息存入redis中 key：register:sms:mobile, value：smsCode
        redisTemplate.opsForValue().set(key, new SendSMSCode(smsCode, new Date().getTime()), 3L, TimeUnit.MINUTES);
        log.info("验证码为：{}, 请在{}分钟内使用", smsCode, 3);
        // 6.保存发送信息到数据库表中
        // TODO 保存到数据库
        // 7.删除图片验证码
        redisTemplate.delete(imageCodeKey);
    }
}
